using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Windows.Forms;
#if POCKETPC
using OpenNETCF.Win32;
#else
using Microsoft.Win32;
#endif

namespace MruMenu
{
    /// <summary>
    /// 
    /// </summary>
	public class MruMenu
	{
        /// <summary>
        /// 
        /// </summary>
		private MenuItem	m_mruRecent=null;
        /// <summary>
        /// 
        /// </summary>
		private ArrayList	m_FilesList=new ArrayList(20);
        /// <summary>
        /// 
        /// </summary>
		private string		registryKeyName="MRUList";
        /// <summary>
        /// 
        /// </summary>
		private int			m_MaxEntries=5;

        /// <summary>
        /// Gets or sets the max entries.
        /// </summary>
        /// <value>The max entries.</value>
		public int MaxEntries
		{
			get
			{
				return m_MaxEntries;
			}
			set
			{
				if ((value<3) && (value>16))
					throw new ArgumentOutOfRangeException("MaxEntries can only be between 3 and 16, not "+value.ToString());
				m_MaxEntries=value;
			}
		}

        /// <summary>
        /// Initializes a new instance of the <see cref="MruMenu"/> class.
        /// </summary>
        /// <param name="mnuRecent">The mnu recent.</param>
        /// <param name="RegKeyName">Name of the reg key.</param>
		public MruMenu(MenuItem mnuRecent, string RegKeyName)
		{
			m_mruRecent=mnuRecent;			
			LoadFromRegistry(RegKeyName);
		}

        /// <summary>
        /// 
        /// </summary>
		public delegate void MRUHandler(int number, string filename);
        /// <summary>
        /// Occurs when [MRU handler event].
        /// </summary>
		public event MRUHandler MRUHandlerEvent;

		#region Registry Methods

        /// <summary>
        /// Gets or sets the name of the registry key.
        /// </summary>
        /// <value>The name of the registry key.</value>
		public String RegistryKeyName
		{
			get
			{
				return registryKeyName;
			}
			set
			{
				registryKeyName = value.Trim();
				if (registryKeyName.Length == 0)
				{
					registryKeyName = null;
				}
			}
		}

        /// <summary>
        /// Loads from registry.
        /// </summary>
        /// <param name="keyName">Name of the key.</param>
		public void LoadFromRegistry(String keyName)
		{
			RegistryKeyName = keyName;
			LoadFromRegistry();
		}

        /// <summary>
        /// Loads from registry.
        /// </summary>
		public void LoadFromRegistry()
		{
			if (registryKeyName != null)
			{
				this.m_FilesList.Clear();

				RegistryKey regKey = Registry.CurrentUser.OpenSubKey(registryKeyName);
				if (regKey != null)
				{
                    try
                    {
                        UInt32 maxEntry = (UInt32)regKey.GetValue("max", m_MaxEntries);
                        m_MaxEntries = Convert.ToInt32(maxEntry);

                        for (int number = m_MaxEntries; number > 0; number--)
                        {
                            String filename = (String)regKey.GetValue("File" + number.ToString());
                            if (filename != null)
                                AddFile(filename);
                        }
                        regKey.Close();
                    }
                    catch
                    {
                    }
				}
			}
			Rebuild();
		}

        /// <summary>
        /// Saves to registry.
        /// </summary>
        /// <param name="keyName">Name of the key.</param>
		public void SaveToRegistry(String keyName)
		{
			RegistryKeyName = keyName;
			SaveToRegistry();
		}

        /// <summary>
        /// Saves to registry.
        /// </summary>
		public void SaveToRegistry()
		{
			if (registryKeyName != null)
			{
				RegistryKey regKey = Registry.CurrentUser.CreateSubKey(registryKeyName);
				if (regKey != null)
				{
					regKey.SetValue("max", m_MaxEntries);
					
					int number = 1;
					foreach(string s in m_FilesList)
					{
						regKey.SetValue("File" + number.ToString(), s);
						number++;
					}
					for (; number <= 16; number++)
					{
						regKey.DeleteValue("File" + number.ToString(), false);
					}
					regKey.Close();
				}
			}
		}

		#endregion

        /// <summary>
        /// Adds the file.
        /// </summary>
        /// <param name="FileName">Name of the file.</param>
		public void AddFile(string FileName)
		{
			if (m_FilesList.Count>=m_MaxEntries)
			{
				// Remove last item is list is full
				m_FilesList.RemoveAt(m_FilesList.Count-1);
			}
				
			if (!File.Exists(FileName))
				return;
			int idx=GetFileIndex(FileName);
			if (idx!=-1)
				return;
			m_FilesList.Insert(0,FileName);	
			Rebuild();
		}

        /// <summary>
        /// Removes the file.
        /// </summary>
        /// <param name="FileName">Name of the file.</param>
		public void RemoveFile(string FileName)
		{
			int idx=GetFileIndex(FileName);
			if (idx==-1)
				return;
			m_FilesList.RemoveAt(idx);
			Rebuild();
		}

        /// <summary>
        /// Gets the index of the file.
        /// </summary>
        /// <param name="FileName">Name of the file.</param>
        /// <returns></returns>
		public int GetFileIndex(string FileName)
		{
			int Count=-1;
			string FileNameEx=FileName.ToLower();
			foreach(string sFName in m_FilesList)
			{
				Count++;
				if (sFName.ToLower()==FileNameEx)
					return Count;
			}
			return -1;
		}

        /// <summary>
        /// Makes the file first.
        /// </summary>
        /// <param name="FileName">Name of the file.</param>
		public void MakeFileFirst(string FileName)
		{
			int idx=GetFileIndex(FileName);
			if (idx==-1)
				return;

			if (idx!=0)
			{
				RemoveFile(FileName);
				m_FilesList.Insert(0,FileName);
			}
			Rebuild();
		}

		private void Rebuild()
		{
			m_mruRecent.MenuItems.Clear();
			int Count=1;
			foreach(string s in m_FilesList)
			{
				MenuItemEx mnuItem=new MenuItemEx();
				mnuItem.Text=ShortenPathname(s,26);				
				mnuItem.Enabled=true;				
				mnuItem.Click+=new EventHandler(mnuItem_Click);
				mnuItem.FileNumber=Count;
				mnuItem.FileName=s;
				m_mruRecent.MenuItems.Add(mnuItem);
				Count++;
			}
		}

		// Shortens a pathname by either removing consecutive components of a path
		// and/or by removing characters from the end of the filename and replacing
		// then with three elipses (...)
		//
		// In all cases, the root of the passed path will be preserved in it's entirety.
		//
		// If a UNC path is used or the pathname and maxLength are particularly short,
		// the resulting path may be longer than maxLength.
		//
		// This method expects fully resolved pathnames to be passed to it.
		// (Use Path.GetFullPath() to obtain this.)

        /// <summary>
        /// Shortens the pathname.
        /// </summary>
        /// <param name="pathname">The pathname.</param>
        /// <param name="maxLength">Length of the max.</param>
        /// <returns></returns>
		static public String ShortenPathname(String pathname, int maxLength)
		{
			if (pathname.Length <= maxLength)
				return pathname;

			String root = Path.GetPathRoot(pathname);
			if (root.Length > 3)
				root += Path.DirectorySeparatorChar;

			String[] elements = pathname.Substring(root.Length).Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);

			int filenameIndex = elements.GetLength(0) - 1;

			if (elements.GetLength(0) == 1) // pathname is just a root and filename
			{
				if (elements[0].Length > 5) // long enough to shorten
				{
					// if path is a UNC path, root may be rather long
					if (root.Length + 6 >= maxLength)
					{
						return root + elements[0].Substring(0, 3) + "...";
					}
					else
					{
						return pathname.Substring(0, maxLength - 3) + "...";
					}
				}
			}
			else if ((root.Length + 4 + elements[filenameIndex].Length) > maxLength) // pathname is just a root and filename
			{
				root += "...\\";

				int len = elements[filenameIndex].Length;
				if (len < 6)
					return root + elements[filenameIndex];

				if ((root.Length + 6) >= maxLength)
				{
					len = 3;
				}
				else
				{
					len = maxLength - root.Length - 3;
				}
				return root + elements[filenameIndex].Substring(0, len) + "...";
			}
			else if (elements.GetLength(0) == 2)
			{
				return root + "...\\" + elements[1];
			}
			else
			{
				int len = 0;
				int begin = 0;

				for (int i = 0; i < filenameIndex; i++)
				{
					if (elements[i].Length > len)
					{
						begin = i;
						len = elements[i].Length;
					}
				}

				int totalLength = pathname.Length - len + 3;
				int end = begin + 1;

				while (totalLength > maxLength)
				{
					if (begin > 0)
						totalLength -= elements[--begin].Length - 1;

					if (totalLength <= maxLength)
						break;

					if (end < filenameIndex)
						totalLength -= elements[++end].Length - 1;

					if (begin == 0 && end == filenameIndex)
						break;
				}

				// assemble final string

				for (int i = 0; i < begin; i++)
				{
					root += elements[i] + '\\';
				}

				root += "...\\";

				for (int i = end; i < filenameIndex; i++)
				{
					root += elements[i] + '\\';
				}

				return root + elements[filenameIndex];
			}
			return pathname;
		}


		private void mnuItem_Click(object sender, EventArgs e)
		{
			if (MRUHandlerEvent!=null)
			{
				MenuItemEx mnuItem=(MenuItemEx)sender;
				MRUHandlerEvent(mnuItem.FileNumber,mnuItem.FileName);
			}
		}
	}

    /// <summary>
    /// 
    /// </summary>
	public class MenuItemEx : MenuItem
	{
        /// <summary>
        /// 
        /// </summary>
		public string FileName;
        /// <summary>
        /// 
        /// </summary>
		public int FileNumber;
	}

    /// <summary>
    /// 
    /// </summary>
	public class MruMenuEx
	{
        /// <summary>
        /// 
        /// </summary>
		protected MenuItem			recentFileMenuItem;
        /// <summary>
        /// 
        /// </summary>
		protected ClickedHandler	clickedHandler;
        /// <summary>
        /// 
        /// </summary>
		protected String			registryKeyName;
        /// <summary>
        /// 
        /// </summary>
		protected int				numEntries = 0;
        /// <summary>
        /// 
        /// </summary>
		protected int				maxEntries = 4;
        /// <summary>
        /// 
        /// </summary>
		protected int				maxShortenPathLength = 26;

		#region MruMenuItem

		// The menu may display a shortened or otherwise invalid pathname
		// This class is used to store the actual filename, preferably as
		// a fully resolved name.

        /// <summary>
        /// 
        /// </summary>
		public class MruMenuItem : MenuItem
		{
            /// <summary>
            /// 
            /// </summary>
			protected String filename="";
            /// <summary>
            /// 
            /// </summary>
			public int mruIndex=0;

            /// <summary>
            /// Initializes a new instance of the <see cref="MruMenuItem"/> class.
            /// </summary>
			public MruMenuItem()
			{			
			}

            /// <summary>
            /// Initializes a new instance of the <see cref="MruMenuItem"/> class.
            /// </summary>
            /// <param name="_filename">The _filename.</param>
            /// <param name="entryname">The entryname.</param>
            /// <param name="eventHandler">The event handler.</param>
			public MruMenuItem(String _filename, String entryname, EventHandler eventHandler)
			{
				filename = _filename;
				this.Text=entryname;
				this.Click+=eventHandler;
			}

            /// <summary>
            /// Gets or sets the filename.
            /// </summary>
            /// <value>The filename.</value>
			public String Filename
			{
				get
				{
					return filename;
				}
				set
				{
					filename = value;
				}
			}
		}
		#endregion

		/// <summary>
		/// MruMenu handles a most recently used (MRU) file list.
		/// 
		/// This class shows the MRU list in a popup menu. To display
		/// the MRU list "inline" use MruMenuInline.
		/// 
		/// The class will load the last set of files from the registry
		/// on construction and store them when instructed by the main
		/// program.
		/// 
		/// Internally, this class uses zero-based numbering for the items.
		/// The displayed numbers, however, will start with one.
		/// </summary>

		#region Construction

		protected MruMenuEx()	{}

        /// <summary>
        /// Initializes a new instance of the <see cref="MruMenuEx"/> class.
        /// </summary>
        /// <param name="_recentFileMenuItem">The _recent file menu item.</param>
        /// <param name="_clickedHandler">The _clicked handler.</param>
		public MruMenuEx(MenuItem _recentFileMenuItem, ClickedHandler _clickedHandler)
		{
			Init(_recentFileMenuItem, _clickedHandler, null, false, 4);
		}

        /// <summary>
        /// Initializes a new instance of the <see cref="MruMenuEx"/> class.
        /// </summary>
        /// <param name="_recentFileMenuItem">The _recent file menu item.</param>
        /// <param name="_clickedHandler">The _clicked handler.</param>
        /// <param name="_maxEntries">The _max entries.</param>
		public MruMenuEx(MenuItem _recentFileMenuItem, ClickedHandler _clickedHandler, int _maxEntries)
		{
			Init(_recentFileMenuItem, _clickedHandler, null, false, _maxEntries);
		}

        /// <summary>
        /// Initializes a new instance of the <see cref="MruMenuEx"/> class.
        /// </summary>
        /// <param name="_recentFileMenuItem">The _recent file menu item.</param>
        /// <param name="_clickedHandler">The _clicked handler.</param>
        /// <param name="_registryKeyName">Name of the _registry key.</param>
		public MruMenuEx(MenuItem _recentFileMenuItem, ClickedHandler _clickedHandler, String _registryKeyName)
		{
			Init(_recentFileMenuItem, _clickedHandler, _registryKeyName, true, 4);
		}

        /// <summary>
        /// Initializes a new instance of the <see cref="MruMenuEx"/> class.
        /// </summary>
        /// <param name="_recentFileMenuItem">The _recent file menu item.</param>
        /// <param name="_clickedHandler">The _clicked handler.</param>
        /// <param name="_registryKeyName">Name of the _registry key.</param>
        /// <param name="_maxEntries">The _max entries.</param>
		public MruMenuEx(MenuItem _recentFileMenuItem, ClickedHandler _clickedHandler, String _registryKeyName, int _maxEntries)
		{
			Init(_recentFileMenuItem, _clickedHandler, _registryKeyName, true, _maxEntries);
		}

        /// <summary>
        /// Initializes a new instance of the <see cref="MruMenuEx"/> class.
        /// </summary>
        /// <param name="_recentFileMenuItem">The _recent file menu item.</param>
        /// <param name="_clickedHandler">The _clicked handler.</param>
        /// <param name="_registryKeyName">Name of the _registry key.</param>
        /// <param name="loadFromRegistry">if set to <c>true</c> [load from registry].</param>
		public MruMenuEx(MenuItem _recentFileMenuItem, ClickedHandler _clickedHandler, String _registryKeyName, bool loadFromRegistry)
		{
			Init(_recentFileMenuItem, _clickedHandler, _registryKeyName, loadFromRegistry, 4);
		}

        /// <summary>
        /// Initializes a new instance of the <see cref="MruMenuEx"/> class.
        /// </summary>
        /// <param name="_recentFileMenuItem">The _recent file menu item.</param>
        /// <param name="_clickedHandler">The _clicked handler.</param>
        /// <param name="_registryKeyName">Name of the _registry key.</param>
        /// <param name="loadFromRegistry">if set to <c>true</c> [load from registry].</param>
        /// <param name="_maxEntries">The _max entries.</param>
		public MruMenuEx(MenuItem _recentFileMenuItem, ClickedHandler _clickedHandler, String _registryKeyName, bool loadFromRegistry, int _maxEntries)
		{
			Init(_recentFileMenuItem, _clickedHandler, _registryKeyName, loadFromRegistry, _maxEntries);
		}

        /// <summary>
        /// Inits the specified _recent file menu item.
        /// </summary>
        /// <param name="_recentFileMenuItem">The _recent file menu item.</param>
        /// <param name="_clickedHandler">The _clicked handler.</param>
        /// <param name="_registryKeyName">Name of the _registry key.</param>
        /// <param name="loadFromRegistry">if set to <c>true</c> [load from registry].</param>
        /// <param name="_maxEntries">The _max entries.</param>
		protected void Init(MenuItem _recentFileMenuItem, ClickedHandler _clickedHandler, String _registryKeyName, bool loadFromRegistry, int _maxEntries)
		{
			if (_recentFileMenuItem == null)
				throw new ArgumentNullException("recentFileMenuItem");

			if (_recentFileMenuItem.Parent == null)
				throw new ArgumentException("recentFileMenuItem is not part of a menu");
			
			recentFileMenuItem = _recentFileMenuItem;
			recentFileMenuItem.Checked = false;
			recentFileMenuItem.Enabled = false;

			// recentFileMenuItem.DefaultItem = false;

			maxEntries = _maxEntries;
			clickedHandler = _clickedHandler;

			if (_registryKeyName != null)
			{
				RegistryKeyName = _registryKeyName;
				if (loadFromRegistry)
				{
					LoadFromRegistry();
				}
			}
		}
		#endregion

		#region Event Handling

        /// <summary>
        /// 
        /// </summary>
		public delegate void ClickedHandler(int number, String filename);

        /// <summary>
        /// Called when [click].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		protected void OnClick(object sender, System.EventArgs e)
		{
			MruMenuItem menuItem = (MruMenuItem) sender;
            clickedHandler(menuItem.mruIndex - StartIndex, menuItem.Filename);
		}
		#endregion

		#region Properties

        /// <summary>
        /// Gets the menu items.
        /// </summary>
        /// <value>The menu items.</value>
		public virtual Menu.MenuItemCollection MenuItems
		{
			get
			{
				return recentFileMenuItem.MenuItems;
			}
		}

        /// <summary>
        /// Gets the start index.
        /// </summary>
        /// <value>The start index.</value>
		public virtual int StartIndex
		{
			get
			{
				return 0;
			}
		}

        /// <summary>
        /// Gets the end index.
        /// </summary>
        /// <value>The end index.</value>
		public virtual int EndIndex
		{
			get
			{
				return numEntries;
			}
		}

        /// <summary>
        /// Gets the num entries.
        /// </summary>
        /// <value>The num entries.</value>
		public int NumEntries
		{
			get 
			{
				return numEntries; 
			}
		}

        /// <summary>
        /// Gets or sets the max entries.
        /// </summary>
        /// <value>The max entries.</value>
		public int MaxEntries
		{
			get 
			{
				return maxEntries; 
			}
			set 
			{
				if (value > 16)
				{
					maxEntries = 16;
				}
				else
				{
					maxEntries = value < 4 ? 4 : value;

					int index = StartIndex + maxEntries;
					while (numEntries > maxEntries)
					{
						MenuItems.RemoveAt(index);
						numEntries--;
					}
				}
			}
		}

        /// <summary>
        /// Gets or sets the length of the max shorten path.
        /// </summary>
        /// <value>The length of the max shorten path.</value>
		public int MaxShortenPathLength
		{
			get
			{
				return maxShortenPathLength;
			}
			set
			{
				maxShortenPathLength = value < 16 ? 16 : value;
			}
		}

		#endregion

		#region Helper Methods

        /// <summary>
        /// Enables this instance.
        /// </summary>
		protected virtual void Enable()
		{
			recentFileMenuItem.Enabled = true;
		}

        /// <summary>
        /// Disables this instance.
        /// </summary>
		protected virtual void Disable()
		{
			recentFileMenuItem.Enabled = false;
			recentFileMenuItem.MenuItems.RemoveAt(0);
		}

        /// <summary>
        /// Sets the first file.
        /// </summary>
        /// <param name="menuItem">The menu item.</param>
		protected virtual void SetFirstFile(MenuItem menuItem)
		{
		}

        /// <summary>
        /// Sets the first file.
        /// </summary>
        /// <param name="number">The number.</param>
		public void SetFirstFile(int number)
		{
			if (number > 0 && numEntries > 1 && number < numEntries)
			{
				MruMenuItem menuItem = (MruMenuItem)MenuItems[StartIndex + number];
                menuItem.mruIndex = StartIndex;
				SetFirstFile(menuItem);
				FixupPrefixes(0);
			}
		}

        /// <summary>
        /// Fixups the entryname.
        /// </summary>
        /// <param name="number">The number.</param>
        /// <param name="entryname">The entryname.</param>
        /// <returns></returns>
		public static String FixupEntryname(int number, String entryname)
		{
			if (number < 9)
				return "&" + (number + 1) + "  " + entryname;
			else if (number == 9)
				return "1&0" + "  " + entryname;
			else
				return (number + 1) + "  " + entryname;
		}

        /// <summary>
        /// Fixups the prefixes.
        /// </summary>
        /// <param name="startNumber">The start number.</param>
		protected void FixupPrefixes(int startNumber)
		{
			if (startNumber < 0)
				startNumber = 0;

			if (startNumber < maxEntries)
			{
				for (int i = StartIndex + startNumber; i < EndIndex; i++, startNumber++)
				{
					MenuItems[i].Text = FixupEntryname(startNumber, MenuItems[i].Text.Substring(startNumber == 9 ? 5 : 4));
				}
			}
		}
		#endregion

		#region Get Methods

        /// <summary>
        /// Finds the filename number.
        /// </summary>
        /// <param name="filename">The filename.</param>
        /// <returns></returns>
		public int FindFilenameNumber(String filename)
		{
			if (filename == null)
				throw new ArgumentNullException("filename");

			if (filename.Length == 0)
				throw new ArgumentException("filename");

			if (numEntries > 0)
			{
				int number = 0;
				for (int i = StartIndex; i < EndIndex; i++, number++)
				{
					if (String.Compare(((MruMenuItem)MenuItems[i]).Filename, filename, true) == 0)
					{
						return number;
					}
				}
			}
			return -1;
		}

        /// <summary>
        /// Finds the index of the filename menu.
        /// </summary>
        /// <param name="filename">The filename.</param>
        /// <returns></returns>
		public int FindFilenameMenuIndex(String filename)
		{
			int number = FindFilenameNumber(filename);
			return number < 0 ? -1 : StartIndex + number;
		}

        /// <summary>
        /// Gets the index of the menu.
        /// </summary>
        /// <param name="number">The number.</param>
        /// <returns></returns>
		public int GetMenuIndex(int number)
		{
			if (number < 0 || number >= numEntries)
				throw new ArgumentOutOfRangeException("number");

			return StartIndex + number;
		}

        /// <summary>
        /// Gets the file at.
        /// </summary>
        /// <param name="number">The number.</param>
        /// <returns></returns>
		public String GetFileAt(int number)
		{
			if (number < 0 || number >= numEntries)
				throw new ArgumentOutOfRangeException("number");

			return ((MruMenuItem)MenuItems[StartIndex + number]).Filename;
		}

        /// <summary>
        /// Gets the files.
        /// </summary>
        /// <returns></returns>
		public String[] GetFiles()
		{
			String[] filenames = new String[numEntries];

			int index = StartIndex;
			for (int i = 0; i < filenames.GetLength(0); i++, index++)
			{
				filenames[i] = ((MruMenuItem)MenuItems[index]).Filename;
			}

			return filenames;
		}

		// This is used for testing
        /// <summary>
        /// Gets the files full entry string.
        /// </summary>
        /// <returns></returns>
		public String[] GetFilesFullEntryString()
		{
			String[] filenames = new String[numEntries];

			int index = StartIndex;
			for (int i = 0; i < filenames.GetLength(0); i++, index++)
			{
				filenames[i] = MenuItems[index].Text;
			}

			return filenames;
		}
		#endregion

		#region Add Methods

        /// <summary>
        /// Sets the files.
        /// </summary>
        /// <param name="filenames">The filenames.</param>
		public void SetFiles(String[] filenames)
		{
			RemoveAll();
			for (int i = filenames.GetLength(0) - 1; i >= 0; i--)
			{
				AddFile(filenames[i]);
			}
		}

        /// <summary>
        /// Adds the files.
        /// </summary>
        /// <param name="filenames">The filenames.</param>
		public void AddFiles(String[] filenames)
		{
			for (int i = filenames.GetLength(0) - 1; i >= 0; i--)
			{
				AddFile(filenames[i]);
			}
		}

		// Shortens a pathname by either removing consecutive components of a path
		// and/or by removing characters from the end of the filename and replacing
		// then with three elipses (...)
		//
		// In all cases, the root of the passed path will be preserved in it's entirety.
		//
		// If a UNC path is used or the pathname and maxLength are particularly short,
		// the resulting path may be longer than maxLength.
		//
		// This method expects fully resolved pathnames to be passed to it.
		// (Use Path.GetFullPath() to obtain this.)

        /// <summary>
        /// Shortens the pathname.
        /// </summary>
        /// <param name="pathname">The pathname.</param>
        /// <param name="maxLength">Length of the max.</param>
        /// <returns></returns>
		static public String ShortenPathname(String pathname, int maxLength)
		{
			if (pathname.Length <= maxLength)
				return pathname;

			String root = Path.GetPathRoot(pathname);
			if (root.Length > 3)
				root += Path.DirectorySeparatorChar;

			String[] elements = pathname.Substring(root.Length).Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);

			int filenameIndex = elements.GetLength(0) - 1;

			if (elements.GetLength(0) == 1) // pathname is just a root and filename
			{
				if (elements[0].Length > 5) // long enough to shorten
				{
					// if path is a UNC path, root may be rather long
					if (root.Length + 6 >= maxLength)
					{
						return root + elements[0].Substring(0, 3) + "...";
					}
					else
					{
						return pathname.Substring(0, maxLength - 3) + "...";
					}
				}
			}
			else if ((root.Length + 4 + elements[filenameIndex].Length) > maxLength) // pathname is just a root and filename
			{
				root += "...\\";

				int len = elements[filenameIndex].Length;
				if (len < 6)
					return root + elements[filenameIndex];

				if ((root.Length + 6) >= maxLength)
				{
					len = 3;
				}
				else
				{
					len = maxLength - root.Length - 3;
				}
				return root + elements[filenameIndex].Substring(0, len) + "...";
			}
			else if (elements.GetLength(0) == 2)
			{
				return root + "...\\" + elements[1];
			}
			else
			{
				int len = 0;
				int begin = 0;

				for (int i = 0; i < filenameIndex; i++)
				{
					if (elements[i].Length > len)
					{
						begin = i;
						len = elements[i].Length;
					}
				}

				int totalLength = pathname.Length - len + 3;
				int end = begin + 1;

				while (totalLength > maxLength)
				{
					if (begin > 0)
						totalLength -= elements[--begin].Length - 1;

					if (totalLength <= maxLength)
						break;

					if (end < filenameIndex)
						totalLength -= elements[++end].Length - 1;

					if (begin == 0 && end == filenameIndex)
						break;
				}

				// assemble final string

				for (int i = 0; i < begin; i++)
				{
					root += elements[i] + '\\';
				}

				root += "...\\";

				for (int i = end; i < filenameIndex; i++)
				{
					root += elements[i] + '\\';
				}

				return root + elements[filenameIndex];
			}
			return pathname;
		}

        /// <summary>
        /// Adds the file.
        /// </summary>
        /// <param name="filename">The filename.</param>
		public void AddFile(String filename)
		{
			String pathname = Path.GetFullPath(filename);
			AddFile(pathname, ShortenPathname(pathname, MaxShortenPathLength));			
		}

        /// <summary>
        /// Adds the file.
        /// </summary>
        /// <param name="filename">The filename.</param>
        /// <param name="entryname">The entryname.</param>
		public void AddFile(String filename, String entryname)
		{
			if (filename == null)
				throw new ArgumentNullException("filename");

			if (filename.Length == 0)
				throw new ArgumentException("filename");

			// Do not Add files which do not exist any more - Happens on LoadFromRegistry
			if (!File.Exists(filename))
				return;

			if (numEntries > 0)
			{
				int index = FindFilenameMenuIndex(filename);
				if (index >= 0)
				{
					SetFirstFile(index - StartIndex);
					return;
				}
			}

			if (numEntries < maxEntries)
			{
				MruMenuItem menuItem = new MruMenuItem(filename, FixupEntryname(0, entryname), new System.EventHandler(OnClick));
				// MenuItems.Add(StartIndex, menuItem);
				MenuItems.Add(menuItem);
				SetFirstFile(menuItem);

				if (numEntries++ == 0)
				{
					Enable();
				}
				else
				{
					FixupPrefixes(1);
				}
			}
			else if (numEntries > 1)
			{
				MruMenuItem menuItem = (MruMenuItem) MenuItems[StartIndex + numEntries - 1];
				menuItem.Text = FixupEntryname(0, entryname);
				menuItem.Filename = filename;
                menuItem.mruIndex = StartIndex;
				SetFirstFile(menuItem);
				FixupPrefixes(1);
			}			
		}

		#endregion

		#region Remove Methods

        /// <summary>
        /// Removes the file.
        /// </summary>
        /// <param name="number">The number.</param>
		public void RemoveFile(int number)
		{
			if (number >= 0 && number < numEntries)
			{
				if (--numEntries == 0)
				{
					Disable();
				}
				else
				{
					int startIndex = StartIndex;
					if (number == 0)
					{
						SetFirstFile(MenuItems[startIndex + 1]);
					}

					MenuItems.RemoveAt(startIndex + number);

					if (number < numEntries)
					{
						FixupPrefixes(number);
					}
				}
			}
		}

        /// <summary>
        /// Removes the file.
        /// </summary>
        /// <param name="filename">The filename.</param>
		public void RemoveFile(String filename)
		{
			if (numEntries > 0)
			{
				RemoveFile(FindFilenameNumber(filename));
			}
		}

        /// <summary>
        /// Removes all.
        /// </summary>
		public void RemoveAll()
		{
			if (numEntries > 0)
			{
				for (int index = EndIndex - 1; index > StartIndex; index--)
				{
					MenuItems.RemoveAt(index);
				}
				Disable();
				numEntries = 0;
			}
		}

		#endregion

		#region Registry Methods

        /// <summary>
        /// Gets or sets the name of the registry key.
        /// </summary>
        /// <value>The name of the registry key.</value>
		public String RegistryKeyName
		{
			get
			{
				return registryKeyName;
			}
			set
			{
				registryKeyName = value.Trim();
				if (registryKeyName.Length == 0)
				{
					registryKeyName = null;
				}
			}
		}

        /// <summary>
        /// Loads from registry.
        /// </summary>
        /// <param name="keyName">Name of the key.</param>
		public void LoadFromRegistry(String keyName)
		{
			RegistryKeyName = keyName;
			LoadFromRegistry();
		}

        /// <summary>
        /// Loads from registry.
        /// </summary>
		public void LoadFromRegistry()
		{
			if (registryKeyName != null)
			{
				RemoveAll();

				RegistryKey regKey = Registry.CurrentUser.OpenSubKey(registryKeyName);
				if (regKey != null)
				{
					UInt32 maxEntry=(UInt32)regKey.GetValue("max", maxEntries);
					maxEntries = Convert.ToInt32(maxEntry);

					for (int number = maxEntries; number > 0; number--)
					{
						String filename = (String)regKey.GetValue("File" + number.ToString());
						if (filename != null)
							AddFile(filename);
					}

					regKey.Close();
				}
			}
		}

        /// <summary>
        /// Saves to registry.
        /// </summary>
        /// <param name="keyName">Name of the key.</param>
		public void SaveToRegistry(String keyName)
		{
			RegistryKeyName = keyName;
			SaveToRegistry();
		}

        /// <summary>
        /// Saves to registry.
        /// </summary>
		public void SaveToRegistry()
		{
			if (registryKeyName != null)
			{
				RegistryKey regKey = Registry.CurrentUser.CreateSubKey(registryKeyName);
				if (regKey != null)
				{
					regKey.SetValue("max", maxEntries);

					int number = 1;
					int i = StartIndex;
					for (; i < EndIndex; i++, number++)
					{
						regKey.SetValue("File" + number.ToString(), ((MruMenuItem)MenuItems[i]).Filename);
					}

					for (; number <= 16; number++)
					{
						regKey.DeleteValue("File" + number.ToString(), false);
					}

					regKey.Close();
				}
			}
		}

		#endregion
	}
}